#ifndef __RESOURCE_MANAGER_HH__
#define __RESOURCE_MANAGER_HH__

//c++
#include <cassert>

//boost
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>

//stl
#include <utility>
#include <stdexcept>

namespace Common
{

namespace Patterns
{

/**
 * @class ResourceHandleTpl declaration of a template for resource handle
 * implementation.
 */
template< typename TResourceType, typename TResourceId >
class ResourceHandleTpl
{
public: // public typedefs
    typedef boost::shared_ptr< ResourceHandleTpl >         Ptr;
    typedef boost::shared_ptr< const ResourceHandleTpl >   PtrC;

public:
    ResourceHandleTpl( const TResourceId& aResourceId ) : iResourceId( aResourceId )
    {
    }

    /**
     * @brief Method that returns resource id of this object
     * @return 
     */
    const TResourceId& getResourceId( void ) const
    {
        return iResourceId;
    }
private:
    /// Resource id
    TResourceId iResourceId;        
};

template< typename TResourceType, typename TResourceId >
class ResourceDescriptorTpl
{
public:
    typedef ResourceHandleTpl< TResourceType, TResourceId > ResourceHandle;
};

/**
 * @class ResourceManager
 * @brief Generic resource manager object.
 * 
 * Depends on policies, uses thread safe policy and resource creator template...
 */
template<
      typename TResourceType
    , typename TResourceId
    , template< typename
              , typename /* pair< TResourceDescriptor, ResourceHandle > */
              > class TResourceCollection
        >
class ResourceManager
{
public: //public typedefs required by the convention code
    typedef boost::shared_ptr< ResourceManager >        Ptr;
    typedef boost::shared_ptr< const ResourceManager >  PtrC;
    typedef boost::weak_ptr< ResourceManager >          WPtr;
    typedef boost::weak_ptr< const ResourceManager >    WPtrC;
    
public: //public typedefs
    typedef TResourceType                                                   ResourceType;
    typedef TResourceId                                                     ResourceId;
    typedef ResourceDescriptorTpl< ResourceType, ResourceId >               ResourceDescriptor;
    typedef typename ResourceDescriptor::ResourceHandle                     ResourceHandle;
    typedef std::pair< ResourceDescriptor, typename ResourceHandle::Ptr >   Collection2nd;
    typedef TResourceCollection< ResourceId
                               , Collection2nd
                               > ResourceCollection;
        
public: //public methods
    /**
     * @brief Acquiring resource.
     * It is acquiring resource handle for a given resource id. It's rather more
     * like a declaration that we are going to use a resource with a given id.
     * To fill it with data it needs to be locked. 
     * 
     * @param ResourceId resource identificator
     * @return ResourceHandle
     */
    const typename ResourceHandle::PtrC& acquire( const ResourceId& aResourceId )
    {        
        //if collection has the resource
        typename ResourceCollection::iterator it = iResourceCollection.find( aResourceId );
        if( it != iResourceCollection.end() )
        {
            //return its handle
            return it->second->second;
        }

        //create pair of required objects
        ResourceDescriptor  lDescriptor;
        typename ResourceHandle::Ptr lHandle( new ResourceHandle( aResourceId ) );

        //insert them to the collection
        iResourceCollection.insert( std::make_pair( lDescriptor, lHandle ) );
                
        //return the handle
        return lHandle;
    }

    /**
     * @brief Lock resource, to read or write
     * @param ResourceHandle handle to a resource that is going to be changed
     */
    ResourceDescriptor lock( const typename ResourceHandle::PtrC& aResourceHandle )
    {
        assert( aResourceHandle );
        
        //check if not locked
        if( !aResourceHandle->locked() )
        {
            findIterator( aResourceHandle.getResourceId() )->second->second->lock();
        }
        
        throw std::logic_error( "Resource handle already locked!" );
    }
    
    /**
     *
     */
    void free( const typename ResourceHandle::PtrC& aResourceHandle )
    {
        //check if not locked
        if( aResourceHandle.locked() )
        {
            throw std::logic_error( "Resource handle locked!" );
        }
        
        iResourceCollection.erase( findIterator( aResourceHandle.getResourceId() ) );
    }


    /**
     *
     */
    void unlockResource( const typename ResourceHandle::PtrC& aResourceHandle )
    {
        if( aResourceHandle->locked() )
        {
            findIterator( aResourceHandle.getResourceId() )->second->second->unlock();
        }
        
        throw std::logic_error( "Resource handle has not been locked!" );
    }


private: // private internal methods
    __inline typename ResourceCollection::iterator findIterator( const ResourceId& aResourceId )
    {
        typename ResourceCollection::iterator it = iResourceCollection.find( aResourceId );
        if( it != iResourceCollection.end() )
        {
            return it;
        }

        throw std::logic_error( "No entry for given resource handle!" );
    }
        
private:
    /// Resource collection instance
    ResourceCollection iResourceCollection;
};
    
} // namespace Patterns

} // namespace Patterns

#endif // __RESOURCE_MANAGER_HH__